
#include "Game.h"
#include "Network.h"

//TODO: These shouldn't be here, find out where to put them
#define KEY_UP 0
#define KEY_DOWN 1
#define KEY_RIGHT 2
#define KEY_LEFT 3

//A game event that occurs on the client
class CClientEvent
{
    
    public:
        
        //Constructor sets default values for events
        CClientEvent() : m_pPlayer(0), m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
        CClientEvent(CPlayer* pPlayer) : m_pPlayer(pPlayer), m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
        
        //Call locally to apply the action of the event and also send it across the network
        void activate()
        {
            onActivate();       //Activate the event locally
            send();             //Send the event over the network
        }
        
        //Call in response to a received packet corresponding to this event
        void respond(RakNet::BitStream& bsData)
        {
            decode(bsData);         //Retrieve the event's data from the packet
            //onRespond();            //Respond to the remote event
            onActivate();
        }
        
        //Writes the event to a bitstream and sends it over the network
        void send();
        
    protected:
        
        //Called when the event is activated locally
        virtual void onActivate() = 0;
        
        //Called when responding to the received remote event
        //virtual void onRespond() = 0;
        
        //Override to write all the event's data to a bitstream and return it
        virtual RakNet::BitStream encode() = 0;
        
        //Override to read all the event's data from the given bitstream
        virtual void decode(RakNet::BitStream& bsData) = 0;
        
        PacketPriority      m_iPriority;
        PacketReliability   m_iReliability;
        
        CPlayer* m_pPlayer;
    
};

//A game event that occurs on the server
class CServerEvent
{
    
    public:
        
        //Constructor sets default values for events
        CServerEvent() : m_pidTarget(UNASSIGNED_PLAYER_ID), m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
        CServerEvent(PlayerID pidTarget) : m_pidTarget(pidTarget), m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
        
        //Call locally to apply the action of the event and also send it across the network,
        //possibly to a specific player.
        void activate()
        {
            onActivate();       //Activate the event locally
            
            if (CGame::getInstance()->getMultiplayer()->isServer())
                send(m_pidTarget);             //Send the event over the network
        }
        
        //Call in response to a received packet corresponding to this event
        void respond(RakNet::BitStream& bsData)
        {
            decode(bsData);         //Retrieve the event's data from the packet
            //onRespond();            //Respond to the remote event
            onActivate();
        }
        
        //Writes the event to a bitstream and sends it over the network
        void send(PlayerID pidPlayer = UNASSIGNED_PLAYER_ID);
        
    protected:
        
        //Called when the event is activated locally
        virtual void onActivate() = 0;
        
        //Called when responding to the received remote event
        //virtual void onRespond() = 0;
        
        //Override to write all the event's data to a bitstream and return it
        virtual RakNet::BitStream encode() = 0;
        
        //Override to read all the event's data from the given bitstream
        virtual void decode(RakNet::BitStream& bsData) = 0;
        
        //TODO: Think about if need more parameters for who to send to, ie. all clients, all but one, one only
        PlayerID 			m_pidTarget;		//UNASSIGNED_PLAYER_ID => all players
        PacketPriority      m_iPriority;
        PacketReliability   m_iReliability;
    
};
/*
class CChangeDirEvent : public CClientEvent
{
    
    public:
        
        CChangeDirEvent()       {}
        CChangeDirEvent(CNetworkObject* pObject, Vector vDir) : m_vDirection(vDir)
        {
            
            m_nidObjectID = pObject->getNetIDGenerator().GetNetworkID();
            
        }
        
    protected:
        
        virtual RakNet::BitStream encode()
        {
            
            RakNet::BitStream bsEvent;
            
            unsigned char cTypeID = ID_CHANGE_DIR;
            bsEvent.Write(cTypeID);         //Write the packet identifier
            bsEvent.Write(m_nidObjectID);   //Write the object's network ID
            //TODO: Replace with write all vector components at once
            bsEvent.Write(m_vDirection[0]); //Write the direction vector
            bsEvent.Write(m_vDirection[1]);
            bsEvent.Write(m_vDirection[2]);
            
            return bsEvent;
            
        }
        
        virtual void decode(RakNet::BitStream& bsData)
        {
            
            bsData.Read(m_nidObjectID);         //Read the network ID of the object to affect
            
            //TODO: Replace with read all vector components at once
            Scalar x, y, z;
            
            bsData.Read(x);         //Read the direction vector
            bsData.Read(y);
            bsData.Read(z);
            
            m_vDirection = Vector(x, y, z);
            
        }
        
        virtual void onActivate()
        {
            
        }
        
        virtual void onRespond()
        {
            
            //TODO: Remove this hack
            //CGame::getInstance()->getMultiplayer()->getNetworkObject(0)->setVel(m_vDirection);
            
            //Get a pointer to the object specified by m_nidObjectID
            CNetworkObject* pObject = (CNetworkObject*) NetworkIDGenerator::GET_OBJECT_FROM_ID(m_nidObjectID);
            
            pObject->setVel(m_vDirection);
            
        }
        
        
    private:
        
        NetworkID m_nidObjectID;
        Vector m_vDirection;
    
};


class CServerInputEvent : public CClientEvent
{
    
    public:
        
        
    
};

class CClientInputEvent : public CServerEvent
{
    
    public:
        
        
    
};

class CLocalEvent
{
    
    public:
        
        void activate();
        
    protected:
        
        virtual void onActivate() = 0;
    
};
*/

//Called by the server after a client has connected and been brought up to speed about the game
//Sends the spawning player's game object's NetworkID to all<?> clients.

//in Server
//		ob = new CCreateObjectEvent( );
//		ob.activate();
//		spawnevent = new CSpawnEvent(playerID, obID);
//		spawnevent.activate();

//Sets a player's avatar object
class CSpawnEvent : public CServerEvent
{
	NetworkID m_nidAvatar;
    
public:
    
    //Constructor for use by a client receiving the event from the server
    CSpawnEvent()       {}
    //Constructor used by the server when creating the event
    CSpawnEvent(PlayerID pidPlayer, NetworkID nidAvatar) : CServerEvent(pidPlayer), m_nidAvatar(nidAvatar) 		{}
    
protected:
    
    //Called when the event is activated locally
    virtual void onActivate()
    {
        
        std::cout << "CSpawnEvent::onActivate(), Avatar ID = " << m_nidAvatar.localSystemId << " , Player ID = " << m_pidTarget.ToString() << std::endl;
        
        //Get a pointer to the avatar object from the NetworkID
        CNetworkObject* pAvatar = (CNetworkObject*) NetworkIDGenerator::GET_OBJECT_FROM_ID(m_nidAvatar);
        
        CPlayer* pPlayer;
        
        //TODO: Possibly fix this by having a list of players on the client
        if (CGame::getInstance()->getMultiplayer()->isServer())
        	pPlayer = CGame::getInstance()->getMultiplayer()->getPlayer(m_pidTarget);
        else
        	pPlayer = CGame::getInstance()->getMultiplayer()->getPlayer();
        
        pPlayer->setObject(pAvatar);
        
    }
    
    //Called when responding to the received remote event
    //virtual void onRespond() = 0;
    
    virtual RakNet::BitStream encode()
    {
        
        RakNet::BitStream bsEvent;
        
        unsigned char cTypeID = ID_SPAWN;
        bsEvent.Write(cTypeID);         //Write the packet identifier
        bsEvent.Write(m_nidAvatar);   //Write the object's network ID
        
        return bsEvent;
        
    }
    
    virtual void decode(RakNet::BitStream& bsData)
    {
        
        bsData.Read(m_nidAvatar);         //Read the network ID of the object to affect
        
    }
};

class CChangeVelEvent : public CServerEvent
{
    
    public:
        
        CChangeVelEvent()       {}
        
        CChangeVelEvent(NetworkID nidObjectID, Vector vVel) : m_nidObjectID(nidObjectID), m_vVel(vVel)
        {
            
            //m_nidObjectID = pObject->getNetIDGenerator().GetNetworkID();
            
        }
        
    protected:
        
        //Called when the event is activated locally
        virtual void onActivate()
        {
            
            //Get a pointer to the object specified by m_nidObjectID
            CNetworkObject* pObject = (CNetworkObject*) NetworkIDGenerator::GET_OBJECT_FROM_ID(m_nidObjectID);
            
            std::cout << "CChangeVelEvent::onActivate pObject = " << pObject << std::endl;
            
            pObject->setVel(m_vVel);
            
        }
        
        //Called when responding to the received remote event
        //virtual void onRespond() = 0;
        
        virtual RakNet::BitStream encode()
        {
            
            RakNet::BitStream bsEvent;
            
            unsigned char cTypeID = ID_CHANGE_VEL;
            bsEvent.Write(cTypeID);         //Write the packet identifier
            bsEvent.Write(m_nidObjectID);   //Write the object's network ID
            //TODO: Replace with write all vector components at once
            bsEvent.Write(m_vVel[0]); //Write the direction vector
            bsEvent.Write(m_vVel[1]);
            bsEvent.Write(m_vVel[2]);
            
            return bsEvent;
            
        }
        
        virtual void decode(RakNet::BitStream& bsData)
        {
            
            bsData.Read(m_nidObjectID);         //Read the network ID of the object to affect
            
            //TODO: Replace with read all vector components at once
            Scalar x, y, z;
            
            bsData.Read(x);         //Read the direction vector
            bsData.Read(y);
            bsData.Read(z);
            
            m_vVel = Vector(x, y, z);
            
        }
    
    private:
        
        NetworkID m_nidObjectID;
        Vector m_vVel;
    
};

class CStartStopEvent : public CClientEvent
{
    
    public:
        
        //Called by the server when it reads it in from a packet
        CStartStopEvent(CPlayer* pPlayer) : CClientEvent(pPlayer)       {}
        
        //Called by the client
        CStartStopEvent(int iKey, bool bStart)
         : CClientEvent(CGame::getInstance()->getMultiplayer()->getPlayer()), m_iKey(iKey), m_bStart(bStart)
        {
            
            //TODO: Replace this with an assert as it should be pretty definite
            if (0 == m_pPlayer)
            	std::cerr << "CStartStopEvent Error: Client's CPlayer instance not found. Program will likely crash now. Sorry." << std::endl;
            
            //Call the base class constructor with the client's known CPlayer object
            //::CClientEvent();
            
            //m_nidObjectID = pObject->getNetIDGenerator().GetNetworkID();
            
        }
        
    protected:
        
        //Called when the event is activated locally
        virtual void onActivate()
        {
            
            //CPlayer* pPlayer = CGame::getInstance()->getMultiplayer()->getPlayer(m_PlayerID);
            //std::cout << "Player pointer = " << pPlayer << std::endl;
            
            //m_nidObjectID.localSystemId
            std::cout << "CStartStopEvent::onActivate PlayerID = " << m_pPlayer->getID().ToString() << std::endl;
            
            if (0 == m_pPlayer->getObject()) {
				std::cerr << "CStartStopEvent Error: Client CPlayer instance does not have a corresponding game object. Cancelling event activation." << std::endl;
				return;
			}
            
            NetworkID playerObjectID = m_pPlayer->getObject()->getNetIDGenerator().GetNetworkID();
            
            //This switch is where all the real code goes
            switch (m_iKey) {
                case KEY_UP : {
						CChangeVelEvent serverEvent(playerObjectID, Vector (0, 0.0005));
                        serverEvent.activate();
                        std::cout << "Up change vel event" << std::endl;
                    }
                    break;
                case KEY_DOWN : {
                        CChangeVelEvent serverEvent(playerObjectID, Vector (0, -0.0005));
                        serverEvent.activate();
                        std::cout << "Down change vel event" << std::endl;
                    }
                    break;
                case KEY_LEFT : {
                        CChangeVelEvent serverEvent(playerObjectID, Vector (-0.0005, 0));
                        serverEvent.activate();
                        std::cout << "Left change vel event" << std::endl;
                    }
                    break;
                case KEY_RIGHT : {
                        CChangeVelEvent serverEvent(playerObjectID, Vector (0.0005, 0));
                        serverEvent.activate();
                        std::cout << "Right change vel event" << std::endl;
                    }
                    break;
                default : 
                    
                    break;
                    
            }
            
        }
        
        //Called when responding to the received remote event
        //virtual void onRespond() = 0;
        
        virtual RakNet::BitStream encode()
        {
            
            RakNet::BitStream bsEvent;
            
            unsigned char cTypeID = ID_START_STOP;
            bsEvent.Write(cTypeID);         //Write the packet identifier
            //bsEvent.Write(m_nidObjectID);   //Write the object's network ID
            bsEvent.Write(m_iKey);          //Write the key identifier
            bsEvent.Write(m_bStart);        //Write whether it is starting or stopping
            
            return bsEvent;
            
        }
        
        virtual void decode(RakNet::BitStream& bsData)
        {
            
            //bsData.Read(m_nidObjectID);         //Read the network ID of the object to affect
            
            bsData.Read(m_iKey);        //Read the key identifier
            bsData.Read(m_bStart);      //Read whether it is starting or stopping
            
        }
        
    private:
        
        //CNetworkObject* m_pObject;
        //NetworkID m_nidObjectID;
        
        int m_iKey;
        bool m_bStart;
    
};
/*
class CSpawnEvent : public CClientEvent
{
    
public:
    
    virtual RakNet::BitStream encode()
    {
        
        RakNet::BitStream bsEvent;
        
        unsigned char cTypeID = ID_START_STOP;
        bsEvent.Write(cTypeID);         //Write the packet identifier
        bsEvent.Write(m_nidObjectID);   //Write the object's network ID
        bsEvent.Write(m_iKey);          //Write the key identifier
        bsEvent.Write(m_bStart);        //Write whether it is starting or stopping
        
        return bsEvent;
        
    }
    
    virtual void decode(RakNet::BitStream& bsData)
    {
        
        bsData.Read(m_nidObjectID);         //Read the network ID of the object to affect
        
        bsData.Read(m_iKey);        //Read the key identifier
        bsData.Read(m_bStart);      //Read whether it is starting or stopping
        
    }
    
};
*/
//----Events received on...---

//----Server----
//New client connected - Send create events for all network objects
//Client commands


//----Client----
//Connection to server accepted
//Create a network object
//Update a network object



/*class CNetworkEvent
{
    
    public:
        
        virtual ~CNetworkEvent()        {}
        
        void activate()
        {
            
            onActivate();
            send();
            
        }
        void respond(RakNet::BitStream& bsData)
        {
            
            receive(bsData);
            onRespond();
            
        }
        
        //Called when the packet's data is to be sent over the network. Override to write all the event's
        //      data to a bitstream and send it 
        virtual void send() = 0;
        
    protected:
        
        //Called when a packet of the event's type is received. Override to read all the event's data
        //      from the given bitstream
        virtual void receive(RakNet::BitStream& bsData) = 0;
        
        virtual void onActivate() = 0;
        virtual void onRespond() = 0;
        
    private:
        
        
    
};*/


/*
class CChangeDirEvent : public CNetworkEvent
{
    
    public:
        
        CChangeDirEvent()       {}
        CChangeDirEvent(Vector vDir) : m_vDirection(vDir)       {}
        
        virtual void send()
        {
            
            RakNet::BitStream bsEvent;
            
            unsigned char cTypeID = ID_CHANGE_DIR;
            
            bsEvent.Write(cTypeID);
            //TODO: Replace with write all vector components at once
            bsEvent.Write(m_vDirection[0]);
            bsEvent.Write(m_vDirection[1]);
            bsEvent.Write(m_vDirection[2]);
            
            CGame::getInstance()->getMultiplayer()->getClient()->Send(&bsEvent, HIGH_PRIORITY, RELIABLE_ORDERED, 0);
            
        }
        
    protected:
        
        virtual void receive(RakNet::BitStream& bsData)
        {
            
            Scalar x, y, z;
            
            bsData.Read(x);
            bsData.Read(y);
            bsData.Read(z);
            
            m_vDirection = Vector(x, y, z);
            
        }
        
        virtual void onActivate()
        {
            
        }
        
        virtual void onRespond()
        {
            std::cout << "vDirection[0] = " << m_vDirection[0] << std::endl;
            std::cout << "vDirection[1] = " << m_vDirection[1] << std::endl;
            CGame::getInstance()->getMultiplayer()->getNetworkObject(0)->setVel(m_vDirection);
            
        }
        
        
    private:
        
        Vector m_vDirection;
    
};
*/

class CCreateObjectEvent : public CServerEvent
{
    
    public:
        
        CCreateObjectEvent()       {}
        //Create a new object
        CCreateObjectEvent(String sType) : m_sType(sType)       {}
        //Replicate a network object
        CCreateObjectEvent(CNetworkObject* pObject, String sType) : m_pObject(pObject), m_sType(sType)
        {
            m_nidObjectID = m_pObject->getNetIDGenerator().GetNetworkID();
        }
        
        CNetworkObject* getObject()         { return m_pObject; }
        
    protected:
        
        //Called when the event is activated locally
        virtual void onActivate()
        {
            
            m_pObject = CGame::getInstance()->getMultiplayer()->addNetworkObject(m_sType);
            
            CGame::getInstance()->addGameObject(m_pObject);
            
            if (CGame::getInstance()->getMultiplayer()->isServer())
                m_nidObjectID = m_pObject->getNetIDGenerator().GetNetworkID();
            else
                //Set the object's network ID
                m_pObject->getNetIDGenerator().SetNetworkID(m_nidObjectID);
                
            std::cout << "CCreateObjectEvent::onActivate(), Type = " << m_sType << ", object's NetworkID = " << m_nidObjectID.localSystemId << std::endl;
            
        }
        
        //Called when responding to the received remote event
        //virtual void onRespond() = 0;
        
        virtual RakNet::BitStream encode()
        {
            
            RakNet::BitStream bsEvent;
            
            unsigned char cTypeID = ID_CREATE_NETWORKOBJECT;
            bsEvent.Write(cTypeID);
            bsEvent.Write((unsigned short) (m_sType.length() + 1));
            bsEvent.Write(m_sType.c_str(), m_sType.length() + 1);
            bsEvent.Write(m_nidObjectID);
            
            return bsEvent;
            
        }
        
        virtual void decode(RakNet::BitStream& bsData)
        {
            
            unsigned short iTypeLength;
            bsData.Read(iTypeLength);
            char* cpType = new char[iTypeLength];
            bsData.Read(cpType, iTypeLength);
            m_sType = cpType;
            
            //Read the object's network ID
            bsData.Read(m_nidObjectID);
            
        }
        
    private:
        
        CNetworkObject* m_pObject;
        String m_sType;
        NetworkID m_nidObjectID;
    
};
/*
class CCreateObjectEvent : public CNetworkEvent
{
    
    public:
        
        CCreateObjectEvent()       {}
        CCreateObjectEvent(String sType) : m_sType(sType)       {}
        CCreateObjectEvent(CNetworkObject* pObject, String sType) : m_pObject(pObject), m_sType(sType)
        {
            
            m_nidObjectID = m_pObject->getNetIDGenerator().GetNetworkID();
            
        }
        
        CNetworkObject* getObject()         { return m_pObject; }
        
        virtual void send()
        {
            
            RakNet::BitStream bsEvent;
            
            unsigned char cTypeID = ID_CREATE_NETWORKOBJECT;
            
            bsEvent.Write(cTypeID);
            bsEvent.Write((unsigned short) (m_sType.length() + 1));
            bsEvent.Write(m_sType.c_str(), m_sType.length() + 1);
            bsEvent.Write(m_nidObjectID);
            
            CGame::getInstance()->getMultiplayer()->getServer()->Send(&bsEvent, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_PLAYER_ID, true);
            
        }
        
    protected:
        
        //Constructor called when receiving event
        //CCreateObjectEvent(RakNet::BitStream& bsData);
        
        virtual void onActivate();
        
        virtual void receive(RakNet::BitStream& bsData);
        
        virtual void onRespond();
        
    private:
        
        CNetworkObject* m_pObject;
        String m_sType;
        NetworkID m_nidObjectID;
    
};*/
